﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Security.Principal;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using Comdiv.QWeb.Factory;
using Comdiv.QWeb.Files;
using Comdiv.QWeb.Utils;

namespace Comdiv.QWeb.ViewEngine {
	// ReSharper disable InconsistentNaming
	// ReSharper disable MemberCanBePrivate.Global
	// ReSharper disable NotAccessedField.Global
	// ReSharper disable UnusedMember.Global
	// ReSharper disable VirtualMemberNeverOverriden.Global
	// ReSharper disable UnusedParameter.Global
	public class QViewBase : IQView {
		private readonly IList<string> _resourcenames = new List<string>();
		private IDictionary<string, string> _resources;
		protected readonly QViewBinder binder = new QViewBinder();
		private TextWriter mainout;

		protected QWebServiceRegistry registry = new QWebServiceRegistry();

		private StringWriter tempout;
		protected QViewContext viewContext;


		protected ActionDescriptor myAction {

			get { return viewContext.Context.ActionDescriptor; }
		}

		#region IQView Members

		public void SetViewContext(QViewContext vctx) {
			viewContext = vctx;
		}

		public void Process() {
			try {
				Bind();
				prepare();
				outresources();
				render();
			}
			catch (Exception ex) {
				onerror(ex);
				if (viewContext.OutputErrors) {
					viewContext.Output.WriteLine("<div class='qview_error'>Error in " + viewContext.Name + " view : " +
					                             ex.ToString().Replace("<", "&lt;").Replace("\n", "").Replace("\r", "<br/>") + "</div>");
				}
				else {
					throw;
				}
			}
			finally {
				finalize();
				viewContext.ViewDescriptor.ReleaseView(this);
				registry = viewContext.Context.Registry;
			}
		}

		public void Subview(string name, object advanceddata) {
			viewContext.Factory.GetView(viewContext.CreateSubviewContext(name, advanceddata)).Process();
		}

		#endregion


		protected bool asbool(object obj) {

			return obj.toBool();
		}

		protected int asint(object obj)
		{

			return obj.toInt();
		}
		protected DateTime asdate(object obj)
		{
			if(null==obj) {
				return new DateTime();
			}
			if(obj is DateTime) {
				return (DateTime) obj;
			}
			return obj.toDate();
		}

		protected decimal asdecimal(object obj)
		{

			return obj.toDecimal();
		}

		protected string asstr(object obj) {
			return obj.toStr();
		}

		public void RenderLink(string name) {
			var res = viewContext.Context.Registry.FileNameResolver;
			var ext = Path.GetExtension(name);
			if (null == ext) throw new NullReferenceException("ext");
			var url = res.Resolve(
				viewContext.Context, FileResolveResultType.LocalUrl,
				true, FileNameResolverExtensions.GetDefaultProbesPaths(ext), new[] {name}, null);
			if (url.noContent()) {
				outpf("<!-- lost link to {0} no resource found on server -->", name);
			}
			else if (ext.ToUpper() == ".JS") {
				outpf("<script type='text/javascript' src='{0}'></script>", url);
			}
			else if (ext.ToUpper() == ".CSS") {
				outpf("<link rel='stylesheet' href='{0}' />", url);
			}
			else {
				outpf("<link type='text/{0}' href='{1}' />", ext.Substring(1), url);
			}
		}

		public string getResource(string name, string lang = null) {
			//if (lang == null) throw new ArgumentNullException("lang");
			lang = lang.hasContent() ? lang : getCurrentLang();
			lang = lang.Split('-')[0].ToLower();
			if (null == _resources) {
				_resources = new Dictionary<string, string>();
				loadFromResourceFiles();
				buildResources();
				buildResourcesAdvanced();
			}
			var key = lang + "_" + name;
			if (_resources.ContainsKey(key)) {
				return _resources[key];
			}
			key = "default_" + name;
			if (_resources.ContainsKey(key)) {
				return _resources[key];
			}
			return "";
		}

		protected void loadFromResourceFiles() {
			var ass = GetType().Assembly;
			var resources = ass.GetManifestResourceNames().Where(x => x.EndsWith(".resb")).OrderBy(x => x).ToArray();
			foreach (var resource in resources) {
				var deflang = Regex.Match(resource, @"\.(\w\w)\.").Groups[1].Value;
				using (var s = ass.GetManifestResourceStream(resource)) {
					var x = XElement.Load(s);
					foreach (var e in x.Elements()) {
						var name = e.Name.LocalName;
						foreach (var a in e.Attributes()) {
							addResource(name, a.Name.LocalName == "code" ? deflang : a.Name.LocalName, a.Value);
						}
					}
				}
			}
		}

		public void addResource(string name, string lang, string value) {
			var name_ = lang.ToLower() + "_" + name;
			_resources[name_] = value;
			if (!_resourcenames.Contains(name)) {
				_resources["default_" + name] = value;
				_resourcenames.Add(name);
			}
		}


		protected virtual void buildResources() {

			//in VBXL
		}

		protected virtual void buildResourcesAdvanced() // in codebehind
		{
		}


		public string getCurrentLang() {
			try {
				return viewContext.Context.Language.Substring(0, 2);
			}
			catch {
				return CultureInfo.CurrentCulture.Name;
			}
		}

		public void Bind() {
			binder.Bind(this, viewContext);
			mainout = viewContext.Output;
			onbind();
		}


		protected void onbind() {

		}

		protected string esc(object s) {
			return s.toStr()
				.Replace("\"", "&quot;")
				.Replace("'", "&apos;")
				.Replace("&", "&amp;")
				.Replace("<", "&lt;")
				.Replace("<", "&gt;");
		}

		protected object getShared(string name) {
			return getShared<object>(name);
		}

		protected T getShared<T>(string name) {
			return viewContext.GetShared<T>(name);
		}

		protected void setShared(string name, object value) {
			viewContext.SetShared(name, value);
		}

		protected bool inroles(params string[] roles) {
			return roles.Any(role => inrole(role));
		}

		protected void enterTemporaryOutput() {
			tempout = new StringWriter();
			mainout = viewContext.Output;
			viewContext.Output = tempout;
		}

		protected string getTemporaryOutput() {
			viewContext.Output = mainout;
			return tempout.ToString();
		}

		protected bool inrole(string role, string usr = null, bool exact = false) {
			var u = viewContext.Context.Registry.PrincipalSource.CurrentUser;
			if (!string.IsNullOrWhiteSpace(usr)) {
				u = new GenericPrincipal(new GenericIdentity(usr), new string[] {});
			}
			return viewContext.Context.Registry.RoleResolver.IsInRole(u, role, exact, viewContext.Context);
		}

		protected void sub(string name, object advanceddata = null) {
			Subview(name, advanceddata);
		}


		protected void outresources() {
			var a = GetType().Assembly;
			var resources = a.GetManifestResourceNames().Where(x => x.StartsWith("_view_resource_"));
			foreach (var resource in resources) {
				using (var s = a.GetManifestResourceStream(resource)) {
					if(null==s)throw new NullReferenceException("resource stream");
					outp(new StreamReader(s).ReadToEnd());
				}
			}
		}

		protected virtual void finalize() {
		}


		protected virtual void onerror(Exception exception) {

		}

		protected virtual void render() {
		}

		protected virtual void prepare() {
		}

		public void RenderChild() {
			viewContext.Factory.GetView(viewContext.ChildContext).Process();
		}

		public virtual string GetCacheKey() {
			return null;
		}

		protected void outpf(string str, params object[] parameters) {
			outp(string.Format(str, parameters));
		}


		protected void outp(string s1) {
			viewContext.Output.Write(s1);
		}

		protected void outp(string s1, string s2) {
			viewContext.Output.Write(s1);
			viewContext.Output.Write(s2);
		}

		protected void outp(string s1, string s2, string s3) {
			viewContext.Output.Write(s1);
			viewContext.Output.Write(s2);
			viewContext.Output.Write(s3);
		}

		protected void outp(params object[] data) {
			if (null != data) {
				foreach (var o in data) {
					viewContext.Output.Write(o.toStr());
				}
			}
		}

		protected void outp(params string[] data) {
			if (null != data) {
				foreach (var o in data) {
					viewContext.Output.Write(o);
				}
			}
		}
	}
	// ReSharper restore UnusedParameter.Global
	// ReSharper restore VirtualMemberNeverOverriden.Global
	// ReSharper restore UnusedMember.Global
	// ReSharper restore NotAccessedField.Global
	// ReSharper restore MemberCanBePrivate.Global
	// ReSharper restore InconsistentNaming
}